맨위로가기

빌더 패턴

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

빌더 패턴은 복잡한 객체의 생성 과정을 단계별로 추상화하여, 객체의 내부 표현을 다양하게 변경하고 생성 과정의 단계를 제어할 수 있도록 하는 디자인 패턴이다. 이 패턴은 객체 생성 코드와 표현 코드를 분리하여 캡슐화하고, 각 제품 유형별로 별도의 구체적인 빌더를 생성해야 하는 단점이 있다. 빌더 패턴은 객체 생성의 유연성을 높이기 위해 전략 패턴과 함께 사용되거나, 복잡한 구조를 가진 객체를 효율적으로 생성하기 위해 컴포지트 패턴과 함께 사용될 수 있다.

더 읽어볼만한 페이지

  • 소프트웨어 디자인 패턴 - 모델-뷰-컨트롤러
    모델-뷰-컨트롤러(MVC)는 소프트웨어 디자인 패턴으로, 응용 프로그램을 모델, 뷰, 컨트롤러 세 가지 요소로 분리하여 개발하며, 사용자 인터페이스 개발에서 데이터, 표현 방식, 사용자 입력 처리를 분리해 유지보수성과 확장성을 높이는 데 기여한다.
  • 소프트웨어 디자인 패턴 - 스케줄링 (컴퓨팅)
    스케줄링은 운영 체제가 시스템의 목적과 환경에 맞춰 작업을 관리하는 기법으로, 장기, 중기, 단기 스케줄러를 통해 프로세스를 선택하며, CPU 사용률, 처리량 등을 기준으로 평가하고, FCFS, SJF, RR 등의 알고리즘을 사용한다.
빌더 패턴

2. 구조


  • '''빌더'''
  • * 객체(제품)를 생성하기 위한 추상 인터페이스이다.
  • '''구체 빌더'''
  • * 빌더에 대한 구현을 제공한다. 이것은 다른 객체를 생성할 수 있는 객체이다. 객체를 만들기 위해 부품을 조립하고 생성한다.


빌더 패턴의 클래스 다이어그램은 다음과 같다.

[1]

UML 클래스 다이어그램에서, `Director` 클래스는 `ProductA1` 및 ProductB1 객체를 직접 생성하고 조립하지 않는다. 대신, Director는 복잡한 객체의 부품을 빌드(생성 및 조립)하기 위해 Builder 인터페이스를 참조한다. 이는 Director가 어떤 구체적인 클래스가 인스턴스화되는지(어떤 표현이 생성되는지)와 독립적으로 만들어준다. Builder1 클래스는 ProductA1ProductB1 객체를 생성하고 조립하여 Builder 인터페이스를 구현한다.




UML 시퀀스 다이어그램은 런타임 상호 작용을 보여준다. Director 객체는 Builder1 객체에서 buildPartA()를 호출하며, 이는 ProductA1 객체를 생성하고 조립한다. 그 후, DirectorBuilder1에서 buildPartB()를 호출하며, 이는 ProductB1 객체를 생성하고 조립한다.[3]

3. 장점 및 단점

빌더 패턴은 다음과 같은 장점을 가진다.[2]


  • 제품의 내부 표현을 다양하게 변경할 수 있다.
  • 생성 및 표현에 대한 코드를 캡슐화한다.
  • 생성 과정의 단계를 제어할 수 있다.


빌더 패턴은 복잡한 객체를 생성하는 클래스를 단순화할 수 있게 해준다.

3. 1. 장점

빌더 패턴은 다음과 같은 문제를 해결한다.[1]

  • 클래스(동일한 생성 과정)가 복잡한 객체의 서로 다른 표현을 어떻게 생성할 수 있는가?
  • 복잡한 객체를 생성하는 기능을 포함하는 클래스를 어떻게 단순화할 수 있는가?


클래스 내에서 복잡한 객체의 부분을 직접 생성하고 조립하는 것은 유연성이 떨어진다. 이는 클래스를 복잡한 객체의 특정 표현을 생성하도록 고정시키고, 나중에 클래스를 변경하지 않고도 (독립적으로) 표현을 변경하는 것을 불가능하게 만든다.

빌더 디자인 패턴은 이러한 문제를 해결하기 위해 다음과 같은 방법을 제시한다.

  • 복잡한 객체의 부분을 생성하고 조립하는 것을 별도의 `Builder` 객체에 캡슐화한다.
  • 클래스는 객체를 직접 생성하는 대신 `Builder` 객체에 객체 생성을 위임한다.


클래스(동일한 생성 과정)는 서로 다른 `Builder` 객체에 위임하여 복잡한 객체의 서로 다른 표현을 생성할 수 있다. 빌더 디자인 패턴의 의도는 복잡한 객체의 생성 과정을 해당 객체의 표현 방식으로부터 분리하는 것이다. 이렇게 함으로써, 동일한 생성 과정으로 서로 다른 표현 방식을 만들 수 있다.

4. 예제

csharp

///



/// 빌더에 의해 생성된 제품을 나타냅니다.

///


public class Bicycle

{

public Bicycle(string make, string model, string colour, int height)

{

Make = make;

Model = model;

Colour = colour;

Height = height;

}

public string Make { get; set; }

public string Model { get; set; }

public int Height { get; set; }

public string Colour { get; set; }

}

///

/// 빌더 추상화.

///


public interface IBicycleBuilder

{

Bicycle GetResult();

string Colour { get; set; }

int Height { get; set; }

}

///

/// 구체적인 빌더 구현.

///


public class GTBuilder : IBicycleBuilder

{

public Bicycle GetResult()

{

return Height == 29 ? new Bicycle("GT", "Avalanche", Colour, Height) : null;

}

public string Colour { get; set; }

public int Height { get; set; }

}

///

/// 디렉터.

///


public class MountainBikeBuildDirector

{

private IBicycleBuilder _builder;

public MountainBikeBuildDirector(IBicycleBuilder builder)

{

_builder = builder;

}

public void Construct()

{

_builder.Colour = "Red";

_builder.Height = 29;

}

public Bicycle GetResult()

{

return this._builder.GetResult();

}

}

public class Client

{

public void DoSomethingWithBicycles()

{

var director = new MountainBikeBuildDirector(new GTBuilder());

// 디렉터는 제품의 단계별 생성을 제어하고 결과를 반환합니다.

director.Construct();

Bicycle myMountainBike = director.GetResult();

}

}

```

위 예제에서 디렉터는 자전거 인스턴스를 조립하며, 생성을 클라이언트가 디렉터에게 제공한 별도의 빌더 객체에 위임한다.

4. 1. 자바

다음은 Java를 이용한 예시이다. 이 소스 코드는 Java SE 5(J2SE 5.0) 이후 버전에서 작동한다.

```java

enum Material{WOOD, CLAY, CONCRETE, SNOW}

class Building{

private Material base;

private Material frame;

private Material wall;

void setBase(Material m){

this.base = m;

}

void setFrame(Material m){

this.frame = m;

}

void setWall(Material m){

this.wall = m;

}

public String toString(){

return "[Base:" + this.base + ", Frame:" + this.frame + ", Wall:" + this.wall + "]";

}

}

interface Builder{

void buildBase();

void buildFrame();

void buildWall();

Building getResult();

}

class JapaneseHouseBuilder implements Builder{

private Building building;

JapaneseHouseBuilder(){

this.building = new Building();

}

public void buildBase(){

this.building.setBase(Material.CONCRETE);

}

public void buildFrame(){

this.building.setFrame(Material.WOOD);

}

public void buildWall(){

this.building.setWall(Material.CLAY);

}

public Building getResult(){

return this.building;

}

}

class KamakuraBuilder implements Builder{

private Building building;

KamakuraBuilder(){

this.building = new Building();

}

public void buildBase(){

this.building.setBase(Material.SNOW);

}

public void buildFrame(){

this.building.setFrame(Material.SNOW);

}

public void buildWall(){

this.building.setWall(Material.SNOW);

}

public Building getResult(){

return this.building;

}

}

class Director{

private Builder builder;

Director(Builder builder){

this.builder = builder;

}

Building construct(){

this.builder.buildBase();

this.builder.buildFrame();

this.builder.buildWall();

return this.builder.getResult();

}

}

public class BuilderTest{

public static void main(String[] argv){

Director d1 = new Director(new JapaneseHouseBuilder());

Director d2 = new Director(new KamakuraBuilder());

Building b1 = d1.construct();

Building b2 = d2.construct();

System.out.println(b1);

System.out.println(b2);

}

}

```

이 소스 코드는 다음 결과를 출력한다.

```text

[Base:CONCRETE, Frame:WOOD, Wall:CLAY]

[Base:SNOW, Frame:SNOW, Wall:SNOW]

```

`Director` (감독자)의 관리 하에, 각 `Builder` (빌더)가 서로 다른 `Building` (생성물) 인스턴스를 생성하고 있음을 알 수 있다.

4. 2. C#

csharp

// C# 구현 예시.

class Pizza

{

string dough;

string sauce;

string topping;

public Pizza() {}

public void SetDough( string d){ dough = d;}

public void SetSauce( string s){ sauce = s;}

public void SetTopping( string t){ topping = t;}

}

// 추상 빌더

abstract class PizzaBuilder

{

protected Pizza pizza;

public PizzaBuilder(){}

public Pizza GetPizza(){ return pizza; }

public void CreateNewPizza() { pizza = new Pizza(); }

public abstract void BuildDough();

public abstract void BuildSauce();

public abstract void BuildTopping();

}

// 구체적인 빌더

class HawaiianPizzaBuilder : PizzaBuilder

{

public override void BuildDough() { pizza.SetDough("cross"); }

public override void BuildSauce() { pizza.SetSauce("mild"); }

public override void BuildTopping() { pizza.SetTopping("ham+pineapple"); }

}

// 구체적인 빌더

class SpicyPizzaBuilder : PizzaBuilder

{

public override void BuildDough() { pizza.SetDough("pan baked"); }

public override void BuildSauce() { pizza.SetSauce("hot"); }

public override void BuildTopping() { pizza.SetTopping("pepparoni+salami"); }

}

// 디렉터

class Waiter {

private PizzaBuilder pizzaBuilder;

public void SetPizzaBuilder (PizzaBuilder pb) { pizzaBuilder = pb; }

public Pizza GetPizza() { return pizzaBuilder.GetPizza(); }

public void ConstructPizza() {

pizzaBuilder.CreateNewPizza();

pizzaBuilder.BuildDough();

pizzaBuilder.BuildSauce();

pizzaBuilder.BuildTopping();

}

}

// 피자를 주문하는 고객.

class BuilderExample

{

public static void Main(string[] args) {

Waiter waiter = new Waiter();

PizzaBuilder hawaiianPizzaBuilder = new HawaiianPizzaBuilder();

PizzaBuilder spicyPizzaBuilder = new SpicyPizzaBuilder();

waiter.SetPizzaBuilder ( hawaiianPizzaBuilder );

waiter.ConstructPizza();

Pizza pizza = waiter.GetPizza();

}

}

```

```csharp

// C# 예시:

// 빌더에 의해 생성된 제품을 나타냅니다.

public class Bicycle

{

public Bicycle(string make, string model, string colour, int height)

{

Make = make;

Model = model;

Colour = colour;

Height = height;

}

public string Make { get; set; }

public string Model { get; set; }

public int Height { get; set; }

public string Colour { get; set; }

}

// 빌더 추상화.

public interface IBicycleBuilder

{

Bicycle GetResult();

string Colour { get; set; }

int Height { get; set; }

}

// 구체적인 빌더 구현.

public class GTBuilder : IBicycleBuilder

{

public Bicycle GetResult()

{

return Height == 29 ? new Bicycle("GT", "Avalanche", Colour, Height) : null;

}

public string Colour { get; set; }

public int Height { get; set; }

}

// 디렉터.

public class MountainBikeBuildDirector

{

private IBicycleBuilder _builder;

public MountainBikeBuildDirector(IBicycleBuilder builder)

{

_builder = builder;

}

public void Construct()

{

_builder.Colour = "Red";

_builder.Height = 29;

}

public Bicycle GetResult()

{

return this._builder.GetResult();

}

}

public class Client

{

public void DoSomethingWithBicycles()

{

var director = new MountainBikeBuildDirector(new GTBuilder());

// 디렉터는 제품의 단계별 생성을 제어하고 결과를 반환합니다.

director.Construct();

Bicycle myMountainBike = director.GetResult();

}

}

```

위 예제에서 디렉터는 자전거 인스턴스를 조립하며, 생성을 클라이언트가 디렉터에게 제공한 별도의 빌더 객체에 위임한다.

4. 3. C++

cpp

// C++ 구현 예시

// Product (제품)

class Pizza

{

private:

std::string dough;

std::string sauce;

std::string topping;

public:

Pizza() { }

~Pizza() { }

void SetDough(const std::string& d) { dough = d; };

void SetSauce(const std::string& s) { sauce = s; };

void SetTopping(const std::string& t) { topping = t; }

void ShowPizza()

{

std::cout << " Yummy !!!" << std::endl

<< "Pizza with Dough as " << dough

<< ", Sauce as " << sauce

<< " and Topping as " << topping

<< " !!! " << std::endl;

}

};

// Abstract Builder (추상 빌더)

class PizzaBuilder

{

protected:

std::auto_ptr pizza;

public:

PizzaBuilder() {}

virtual ~PizzaBuilder() {}

std::auto_ptr GetPizza() { return pizza; }

void createNewPizzaProduct() { pizza.reset(new Pizza); }

virtual void buildDough() = 0;

virtual void buildSauce() = 0;

virtual void buildTopping() = 0;

};

// ConcreteBuilder (구상 빌더)

class HawaiianPizzaBuilder : public PizzaBuilder

{

public:

HawaiianPizzaBuilder() : PizzaBuilder() {}

~HawaiianPizzaBuilder() {}

void buildDough() { pizza->SetDough("cross"); }

void buildSauce() { pizza->SetSauce("mild"); }

void buildTopping() { pizza->SetTopping("ham and pineapple"); }

};

// ConcreteBuilder (구상 빌더)

class SpicyPizzaBuilder : public PizzaBuilder

{

public:

SpicyPizzaBuilder() : PizzaBuilder() {}

~SpicyPizzaBuilder() {}

void buildDough() { pizza->SetDough("pan baked"); }

void buildSauce() { pizza->SetSauce("hot"); }

void buildTopping() { pizza->SetTopping("pepperoni and salami"); }

};

// Director (감독)

class Waiter

{

private:

PizzaBuilder* pizzaBuilder;

public:

Waiter() : pizzaBuilder(NULL) {}

~Waiter() {}

void SetPizzaBuilder(PizzaBuilder* b) { pizzaBuilder = b; }

std::auto_ptr GetPizza() { return pizzaBuilder->GetPizza(); }

void ConstructPizza()

{

pizzaBuilder->createNewPizzaProduct();

pizzaBuilder->buildDough();

pizzaBuilder->buildSauce();

pizzaBuilder->buildTopping();

}

};

// 고객이 피자를 주문하는 상황.

int main()

{

Waiter waiter; // 웨이터 객체 생성

HawaiianPizzaBuilder hawaiianPizzaBuilder; // 하와이안 피자 빌더 객체 생성

waiter.SetPizzaBuilder(&hawaiianPizzaBuilder); // 웨이터에게 하와이안 피자 빌더 설정

waiter.ConstructPizza(); // 피자 생성

std::auto_ptr pizza = waiter.GetPizza(); // 생성된 피자 가져오기

pizza->ShowPizza(); // 피자 정보 출력

SpicyPizzaBuilder spicyPizzaBuilder; // 스파이시 피자 빌더 객체 생성

waiter.SetPizzaBuilder(&spicyPizzaBuilder); // 웨이터에게 스파이시 피자 빌더 설정

waiter.ConstructPizza(); // 피자 생성

pizza = waiter.GetPizza(); // 생성된 피자 가져오기

pizza->ShowPizza(); // 피자 정보 출력

return EXIT_SUCCESS;

}

4. 4. 펄

perl

# Product

package pizza;

sub new {

return bless {

dough => undef,

sauce => undef,

topping => undef

}, shift;

}

sub set_dough {

my( $self, $dough ) = @_;

$self->{dough} = $dough;

}

sub set_sauce {

my( $self, $sauce ) = @_;

$self->{sauce} = $sauce;

}

sub set_topping {

my( $self, $topping ) = @_;

$self->{topping} = $topping;

}

1;

# Abstract builder

package pizza_builder;

sub new {

return bless {

pizza => undef

}, shift;

}

sub get_pizza {

my( $self ) = @_;

return $self->{pizza};

}

sub create_new_pizza_product {

my( $self ) = @_;

$self->{pizza} = pizza->new;

}

# This is what an abstract method could look like in perl...

sub build_dough {

croak("This method must be overridden.");

}

sub build_sauce {

croak("This method must be overridden.");

}

sub build_topping {

croak("This method must be overridden.");

}

1;

# Concrete builder

package hawaiian_pizza_builder;

use base qw{ pizza_builder };

sub build_dough {

my( $self ) = @_;

$self->{pizza}->set_dough("cross");

}

sub build_sauce {

my( $self ) = @_;

$self->{pizza}->set_sauce("mild");

}

sub build_topping {

my( $self ) = @_;

$self->{pizza}->set_topping("ham+pineapple");

}

1;

# Concrete builder

package spicy_pizza_builder;

use base qw{ pizza_builder };

sub build_dough {

my( $self ) = @_;

$self->{pizza}->set_dough("pan baked");

}

sub build_sauce {

my( $self ) = @_;

$self->{pizza}->set_sauce("hot");

}

sub build_topping {

my( $self ) = @_;

$self->{pizza}->set_topping("pepperoni+salami");

}

1;

# Director

package waiter;

sub new {

return bless {

pizza_builder => undef

}, shift;

}

sub set_pizza_builder {

my( $self, $builder ) = @_;

$self->{pizza_builder} = $builder;

}

sub get_pizza {

my( $self ) = @_;

return $self->{pizza_builder}->get_pizza;

}

sub construct_pizza {

my( $self ) = @_;

$self->{pizza_builder}->create_new_pizza_product;

$self->{pizza_builder}->build_dough;

$self->{pizza_builder}->build_sauce;

$self->{pizza_builder}->build_topping;

}

1;

# Lets order pizza (client of Director/Builder)

package main

my $waiter = waiter->new;

my $hawaiian_pb = hawaiian_pizza_builder->new;

my $spicy_pb = spicy_pizza_builder->new;

$waiter->set_pizza_builder( $hawaiian_pb );

$waiter->construct_pizza;

my $pizza = $waiter->get_pizza;

print "Serving a nice pizza with:\n";

for (keys %$pizza) {

print " $pizza->{$_} $_\n";

}

1;

4. 5. PHP

php

<syntaxhighlight lang="php">

/ Product /

class Pizza{

private $dough;

private $sauce;

private $topping;

public function setDough($dough){

$this->dough = $dough;

}

public function setSauce($sauce){

$this->sauce = $sauce;

}

public function setTopping($topping){

$this->topping = $topping;

}

}

/ Abstract builder /

abstract class PizzaBuilder{

protected $pizza;

public function __construct(){

$this->pizza = new Pizza();

}

public function getPizza(){

return $this->pizza;

}

abstract function buildDough();

abstract function buildSauce();

abstract function buildTopping();

}

/ Concrete builder /

class SpicyPizza extends PizzaBuilder{

public function buildDough(){

$this->pizza->setDough('crispy');

}

public function buildSauce(){

$this->pizza->setSauce('hot');

}

public function buildTopping(){

$this->pizza->setTopping('pepperoni+salami');

}

}

/ Director /

class Chef{

private $pizza_builder;

public function setPizzaBuilder(PizzaBuilder $pizza_builder){

$this->pizza_builder = $pizza_builder;

}

public function cookPizza(){

$this->pizza_builder->buildDough();

$this->pizza_builder->buildSauce();

$this->pizza_builder->buildTopping();

}

public function getPizza(){

return $this->pizza_builder->getPizza();

}

}

//Customer orders a Pizza.

$chef = new Chef();

$order = new SpicyPizza();

$chef->setPizzaBuilder($order);

$chef->cookPizza();

$pizza = $chef->getPizza();

print_r($pizza);

</syntaxhighlight>

5. 활용 사례

빌더 패턴은 다음과 같은 문제들을 해결한다.[1]


  • 클래스(동일한 생성 과정)가 복잡한 객체의 서로 다른 표현을 어떻게 생성할 수 있는가?
  • 복잡한 객체를 생성하는 기능을 포함하는 클래스를 어떻게 단순화할 수 있는가?


클래스 내에서 복잡한 객체의 부분을 직접 생성하고 조립하는 것은 유연성이 떨어진다. 이는 클래스를 복잡한 객체의 특정 표현을 생성하도록 고정시키고, 나중에 클래스를 변경하지 않고도 (독립적으로) 표현을 변경하는 것을 불가능하게 만든다.

빌더 디자인 패턴은 이러한 문제를 해결하는 방법을 설명한다.

  • 복잡한 객체의 부분을 생성하고 조립하는 것을 별도의 `Builder` 객체에 캡슐화한다.
  • 클래스는 객체를 직접 생성하는 대신 `Builder` 객체에 객체 생성을 위임한다.


클래스(동일한 생성 과정)는 서로 다른 `Builder` 객체에 위임하여 복잡한 객체의 서로 다른 표현을 생성할 수 있다. 빌더 디자인 패턴의 의도는 복잡한 객체의 생성 과정을 해당 객체의 표현 방식으로부터 분리하는 것이다. 이렇게 함으로써, 동일한 생성 과정으로 서로 다른 표현 방식을 만들 수 있다.

6. 다른 패턴과의 관계

빌더 패턴은 다른 디자인 패턴과 함께 사용되어 더 큰 유연성과 효율성을 제공한다.


  • '''전략 패턴''': 전략 패턴에서 `빌더`를 `컨텍스트`로 설계하면 인스턴스 생성 과정을 더욱 유연하게 할 수 있다.[1]
  • '''컴포지트 패턴''': 컴포지트와 같이 복잡한 구조를 가진 인스턴스는 빌더 패턴을 응용하여 효율적으로 생성할 수 있다.[1]

참조

[1] 웹사이트 The Builder design pattern - Problem, Solution, and Applicability http://w3sdesign.com[...] 2017-08-13
[2] 웹사이트 Index of /archive/2010/winter/51023-1/presentations https://www.classes.[...] 2016-03-03
[3] 웹사이트 The Builder design pattern - Structure and Collaboration http://w3sdesign.com[...] 2017-08-12



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com